/*
 * file name: maildrop.cpp
 * author: liujianping@snda.com
 * usage:       
 *      maildrop -b <bind.ip.for.delivery> -f <from@address> -t <to@addresss> -T <timeout> [-v]
 *      -b: bind ip for delivery
 *      -f: 发件人地址
 *      -t: 收件人地址
 *      -T: maildrop 最长发送超时时间
 *      -v: verbose the failed reason to the stdout
*/
#include <stdarg.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <iostream>
#include <string>
using namespace std;

#include "conf_parser.h"
#include "signal_helper.h"
using namespace edm::common;

#include "smtp_delivery.h"
#include "maildrop.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//!#define DEBUG

#ifdef DEBUG
#include "log.h"
#endif
const char* helo_host = "edm1.mail.sdo.com";
const char* bind_host = 0;
const char* udp_host = 0;
const char* env_from = 0;
const char* env_to = 0;
const char* env_to_domain = 0;
const char* option = 0;
long  timout = 300;//! default timeout for one mail 60*5 second
static int g_verbose_flag = 1;
static void udp_log(char* fmt, ...);

static void msg_verbose(int verbose)
{
    g_verbose_flag = verbose;
}

static void msg_exit(int code, const char* fmt, ...)
{
    if(g_verbose_flag)
    {
        static char msg[1024];
        va_list va;
        va_start(va, fmt);
        vsnprintf(msg, 1023, fmt, va);
        va_end(va);
        msg[1023] = '\0';
        cout << msg <<endl;
    }
    //! exit limit : 0 -255
    exit(code - 300);
}

int get_domain(const std::string & email, std::string & domain)
{       
    return 0;
}

void sig_user(int sig)
{
    msg_exit(531, "5.3.1 maildrop killed by user");
}

void sig_alarm(int sig)
{
    msg_exit(405, "4.0.5 maildrop process timeout");
}

void udp_log(char* fmt, ...)
{
    static char msg[1024];
    va_list va;
    va_start(va, fmt);
    vsnprintf(msg, 1023, fmt, va);
    va_end(va);
    msg[1023] = '\0';
    
    in_addr_t my_addr = inet_addr(udp_host);
    if(my_addr == INADDR_NONE) return;
    in_addr_t your_addr = inet_addr(bind_host);
    if(your_addr == INADDR_NONE) return;

    struct sockaddr_in dest_addr;
    memset(&dest_addr, 0, sizeof(struct sockaddr_in));
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(5566);
    dest_addr.sin_addr.s_addr = your_addr;

    int s = socket(AF_INET,SOCK_DGRAM,0);
    if(s <= 0) return;

    /*
    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(struct sockaddr_in));
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = 0;
    local_addr.sin_addr.s_addr = my_addr;
    if(bind(s, (struct sockaddr *)&local_addr, sizeof(sockaddr_in))) return;
    */
    
    sendto(s, msg, strlen(msg), 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));
}

//! maildrop -b <bind.ip.for.delivery> -u <udp host> -f <from@address> -t <to@address> -O <path/of/mx_setting/conf> -T <timeout> [-v]
int main(int argc, char** argv)
{
    if(argc < 9) msg_exit(-1, "usage: maildrop -b <bind.ip.for.delivery> -u <udp host> -f <from@address> -t <to@address> -O <path/of/mx_setting/conf> -T <timeout> -h <helo_host> [-v]\n");

    signal_helper_t::sig_catch(SIGALRM, sig_alarm);
    signal_helper_t::sig_catch(SIGUSR1, sig_user);
    //! options analyse
    char ch;
    while ((ch = getopt(argc, argv, "b:u:f:t:O:T:v")) != EOF) {
        switch (ch) {
            case 'b':
                bind_host = optarg;
                break;           
            case 'u':
                udp_host = optarg;
                break;           
            case 'f':
                env_from = optarg;
                break;
            case 't':
                env_to = optarg;
                break;
            case 'O':
                option = optarg;
                break;
            case 'T':
                timout = atol(optarg);
                break;
            case 'h':
                helo_host = optarg;
                break;
            case 'v':
                msg_verbose(1);
                break;
            case '?':
                msg_exit(-1, "4.0.2 maildrop invoked failed");
        }
    }
    if(!bind_host || !env_from || !env_to )
        msg_exit(-1, "4.0.3 maildrop parameter loss");

    //!env_to parse
    char* at = strchr((char*)env_to, '@');
    if(!at)
        msg_exit(-1, "4.0.4 maildrop rcpt to <%s> is not right format", env_to);
    env_to_domain = at + 1;

#ifdef DEBUG
    singleton<Log>::Instance().set_path("./log");
    singleton<Log>::Instance().set_filename("maildrop.log");
    singleton<Log>::Instance().set_maxline(50000);
    singleton<Log>::Instance().set_maxsize(100000);

    int flag_print_file,flag_print_screen,flag_log_level;
    flag_print_file = 0;
    flag_print_screen = 1;
    flag_log_level = 6;

    if(flag_print_file)	singleton<Log>::Instance().enable_print_file(true);	
    if(flag_print_screen)	singleton<Log>::Instance().enable_print_screen(false);

    if( flag_log_level >=1 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_FATAL), true);
    if( flag_log_level >=2 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_ERROR), true);
    if( flag_log_level >=3 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_WARN), true);
    if( flag_log_level >=4 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_INFO), true);
    if( flag_log_level >=5 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_TRACE), true);
    if( flag_log_level >=6 ) singleton<Log>::Instance().enable_log_level(LOG_FLAG(LF_DEBUG), true);	

    singleton<Log>::Instance().enable_log_module(COMMON, true);
    singleton<Log>::Instance().enable_log_module(MAILDROP, true);
    singleton<Log>::Instance().open();	   
#endif    
        
    vector<string> default_mx;
    if(option)
    {
        conf_parser_t conf_parser(':');
        if(conf_parser.open(option))
            msg_exit(-1, "4.0.5 maildrop configure <%s> parsed failed", option);
        
        conf_parser.get_vector_value(env_to_domain, default_mx);
    }
    
    alarm(timout);
    //! smtp delivery
    smtp_delivery_t smtp_delivery(bind_host);
    if(smtp_delivery.send(helo_host,
                          env_from, 
                          env_to, 
                          env_to_domain, 
                          90,//! socket timeout 60 sec 
                          0, 
                          default_mx))
    {
        udp_log("[%s]:[%d]:%s", env_to, smtp_delivery.smtp_code(), smtp_delivery.reason());
        msg_exit(smtp_delivery.status(), smtp_delivery.reason());
    }
    else
        udp_log("[%s]:[%d]:%s", env_to, smtp_delivery.smtp_code(), smtp_delivery.reason());
    
    return 0;
}
